//
//  GameSystem.hpp
//  ComponentSystem
//
//  Created by Jeppe Nielsen on 9/29/14.
//  Copyright (c) 2014 Jeppe Nielsen. All rights reserved.
//

#pragma once
#include "TypeDefs.hpp"

class World;
class GameObject;

class GameSystem {
public:
    
    GameSystem();
    virtual ~GameSystem();
protected:
    virtual void Initialize();
    
    template<class T>
    void AddComponent();
    
    virtual void Update(float dt);
    virtual void Render();
    
    World& World();
    
    virtual int AddObject(GameObject* object);
    virtual int RemoveObject(int index);
    
private:
    ComponentMask aspect;
    class World* world;
    
    typedef std::vector<int> ObjectIndicies;
    
    ObjectIndicies objectIndicies;
    
    friend class World;
};



#define _SYSTEM_NODE1(C1) \
void Initialize() { \
AddComponent<C1>(); \
} \
struct Node { \
GameObject* Object; \
class C1* C1; \
Node(const class GameObject& object) { \
C1 = object.GetComponent<class C1>(); \
}};\

#define _SYSTEM_NODE2(C1, C2) \
void Initialize() { \
AddComponent<C1>(); \
AddComponent<C2>(); \
} \
struct Node { \
GameObject* Object; \
class C1* C1; \
class C2* C2; \
Node(const class GameObject& object) { \
C1 = object.GetComponent<class C1>(); \
C2 = object.GetComponent<class C2>(); \
}};\

#define _SYSTEM_START(NAME) \
class NAME : public GameSystem  { \
public: \

#define _SYSTEM_END(NAME) \
typedef std::vector<Node> NodesList;\
protected: \
void ObjectAdded(const Node& node); \
void ObjectRemoved(const Node& node); \
NodesList nodes;\
int AddObject(const GameObject& object) {\
int size = (int)nodes.size();\
nodes.push_back(Node(object)); \
Node& node = nodes.back(); \
node.Object = *object; \
ObjectAdded(node); \
return size;\
}\
int RemoveObject(int index) {\
Node& removed = nodes[index];\
Node& last = nodes.back();\
nodes[index] = last;\
nodes.pop_back();\
ObjectRemoved(removed); \
return 0; \
}\
protected: \
const NodesList& Nodes() { return nodes; } \

#define _SYSTEM_1(NAME, C1) \
_SYSTEM_START(NAME) \
_SYSTEM_NODE1(C1) \
_SYSTEM_END(NAME) \

#define _SYSTEM_2(NAME, C1, C2) \
_SYSTEM_START(NAME) \
_SYSTEM_NODE2(C1,C2) \
_SYSTEM_END(NAME) \
/*
#define _SYSTEM_3(NAME, C1, C2, C3) \
_SYSTEM_START(NAME) \
_SYSTEM_NODE3(C1,C2,C3) \
_SYSTEM_END(NAME) \

#define _SYSTEM_4(NAME, C1, C2, C3, C4) \
_SYSTEM_START(NAME) \
_SYSTEM_NODE4(C1,C2,C3,C4) \
_SYSTEM_END(NAME) \

#define _SYSTEM_5(NAME, C1, C2, C3, C4, C5) \
_SYSTEM_START(NAME) \
_SYSTEM_NODE5(C1,C2,C3,C4,C5) \
_SYSTEM_END(NAME) \
*/
//#define GET_MACRO(_1,_2,_3,_4,_5, _6, NAME,...) NAME
//#define SYSTEM(...) GET_MACRO(__VA_ARGS__, _SYSTEM_5, _SYSTEM_4, _SYSTEM_3, _SYSTEM_2, _SYSTEM_1)(__VA_ARGS__)
#define GET_MACRO(_1,_2,_3, NAME,...) NAME
#define SYSTEM(...) GET_MACRO(__VA_ARGS__, _SYSTEM_2, _SYSTEM_1)(__VA_ARGS__)


